using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
using System.IO;
using System.IO.Compression;
using System.Data;
using System.Threading;
using System.Net;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Xml.Serialization;

namespace WOWEquipOptimizer
{
    public struct GenericItem
    {
        public int Id;
        public int Gem0Id;
        public int Gem1Id;
        public int Gem2Id;
        public string EnchantName;
    }
    public struct ParsedCharacter
    {
        public GenericItem[] Items;
        public string Class;
        public string Build;
    }
    /// <summary>
    /// our main database, including database updates etc.
    /// </summary>
    public class Database
    {
        private static string ITEMPATH = @"D:\Projekte\ItemParser\xmlitems\";
        private string m_DBFileName = "dbnew.xgz";
        public static DataSet ItemDBSet = new DataSet();
        public static List<Item> Items;
        public Database()
        {
            //updateFiles(ITEMPATH);
            //createXSD();
            //createXMLDB();
            //loadXMLDB();
            //Database.Items = Item.generateEquipList(ref Database.ItemDBSet);
            //parseItemInfo();
            //SaveItemDB();
            LoadItemDB();
        }
        private void LoadItemDB()
        {
            GZipStream stream = new GZipStream(new FileStream("itemdb.xgz", FileMode.Open), CompressionMode.Decompress);
            XmlSerializer serialize = new XmlSerializer(typeof(List<Item>));
            try
            {
                Database.Items = (List<Item>)serialize.Deserialize(stream);
            }
            catch (Exception)
            {
            }
            
            Item.InitFilter();
        }
        private void SaveItemDB()
        {
            GZipStream stream = new GZipStream(new FileStream("itemdb.xgz", FileMode.Create), CompressionMode.Compress);
            XmlSerializer serialize = new XmlSerializer(typeof(List<Item>));
            serialize.Serialize(stream, Database.Items);
            stream.Close();
        }
        private void createXSD()
        {
            XmlSchemaSet schemaSet = new XmlSchemaSet();
            XmlSchemaInference inference = new XmlSchemaInference();
            DirectoryInfo myInfo = new DirectoryInfo(ITEMPATH);
            FileInfo[] myFiles = myInfo.GetFiles("i*.xml");
            int counter = 0;
            for (int i = 0; i < myFiles.Length; i++)
            {
                XPathDocument document = new XPathDocument(myFiles[i].FullName);
                XPathNavigator navigator = document.CreateNavigator();
                if (!navigator.MoveToChild("page", string.Empty)) continue;
                if (!navigator.MoveToFollowing("itemTooltip", string.Empty)) continue;
                if (navigator.MoveToChild("instanceBound", string.Empty))
                {
                    continue;
                }
                if (!navigator.MoveToChild("classId", string.Empty)) continue;
                int type = navigator.ValueAsInt;
                // 0 consumables, 1 bag,2 weapon, 3 gem,4 armor,5 reagents,6 projectile,7 misc?,8 none,9 recipie,
                if (!(type == 2 || type == 3 || type == 4 || type == 7)) continue;
                XmlReader reader = XmlReader.Create(myFiles[i].FullName);
                reader.ReadToFollowing("itemTooltip");
                if (counter == 0)
                {
                    schemaSet = inference.InferSchema(reader);
                }
                else
                {
                    schemaSet = inference.InferSchema(reader, schemaSet);
                }
                counter++;
            }
            schemaSet.Compile();
            XmlSchema[] schemas = new XmlSchema[schemaSet.Schemas().Count];
            XmlTextWriter xWriter = new XmlTextWriter(ITEMPATH + "itemschema.xsd", null);
            schemaSet.Schemas().CopyTo(schemas, 0);
            xWriter.Formatting = Formatting.Indented;
            schemas[schemas.Length - 1].Write(xWriter);
            xWriter.Close();
        }
        private void loadXMLDB()
        {
            ItemDBSet = new DataSet();
            GZipStream stream = new GZipStream(new FileStream(m_DBFileName, FileMode.Open), CompressionMode.Decompress);
            ItemDBSet.ReadXml((Stream)stream);
            stream.Close();
        }
        // classid: stuff=7,projectile=6,trinket/ring/armor=4,weapon=2,container=1,gem=3,reagent=5,recipie=9,consumable=0,misc=15,stuff=12
        // need to parse 2,3,4,7
        private void createXMLDB()
        {
            ItemDBSet = new DataSet();
            ItemDBSet.ReadXmlSchema(ITEMPATH + "itemschema.xsd");
            ItemDBSet.EnforceConstraints = false;
            DirectoryInfo myInfo = new DirectoryInfo(ITEMPATH);
            FileInfo[] myFiles = myInfo.GetFiles("i*.xml");
            foreach (FileInfo myFile in myFiles)
            {
                XPathDocument document = new XPathDocument(myFile.FullName);
                XPathNavigator navigator = document.CreateNavigator();
                if (!navigator.MoveToChild("page", string.Empty)) continue;                
                if (!navigator.MoveToFollowing("itemTooltip", string.Empty)) continue;
                if (navigator.MoveToChild("instanceBound", string.Empty))
                {
                    continue;
                }
                if (!navigator.MoveToChild("classId", string.Empty)) continue;
                int type = navigator.ValueAsInt;
                if (!(type == 2 || type == 3 || type == 4 || type == 7)) continue;
                XmlReader reader = XmlReader.Create(myFile.FullName);
                reader.ReadToFollowing("itemTooltip");
                ItemDBSet.ReadXml(reader);
            }
            GZipStream stream = new GZipStream(new FileStream(m_DBFileName, FileMode.Create), CompressionMode.Compress);
            ItemDBSet.WriteXml(stream, XmlWriteMode.WriteSchema);
            stream.Close();
        }
        public bool parseWeb(int number)
        {
            return parseWeb(number, true, false);
        }
        public bool parseWeb(int number, bool tooltip, bool europe)
        {
            Random myRandom = new Random();
            string woweurope = "wow-europe";
            string wowusa = "worldofwarcraft";
            string wowaddr = wowusa;
            if (europe) wowaddr = woweurope;
            string url = "http://armory." + wowaddr + ".com/item-tooltip.xml?i=";
            string path = ITEMPATH;
            if (!tooltip)
            {
                url = "http://armory."+wowaddr+".com/item-info.xml?i=";
                path = ITEMPATH + "\\info\\";
            }
            try
            {
                HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(@url + number);
                httpRequest.UserAgent = @"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
                httpRequest.Timeout = 1000;
                HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(httpResponse.GetResponseStream());
                xmlDocument.Save(path + "i" + number + ".xml");
                httpResponse.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
            return true;
        }
        // classficiation: 0 trash, 1 elite, 2 named, 3 boss, 4 spawned
        public void parseItemInfo()
        {
            foreach (Item item in Database.Items)
            {
                try
                {
                    XmlReader reader = XmlReader.Create(ITEMPATH + "\\info\\i" + item.Id + ".xml");
                    XPathDocument document = new XPathDocument(reader);
                    XPathNavigator navigator = document.CreateNavigator();
                    navigator.MoveToChild("page", string.Empty);
                    navigator.MoveToChild("itemInfo", string.Empty);
                    navigator.MoveToChild("item", string.Empty);
                    int ilvl = Int32.Parse(navigator.GetAttribute("level", string.Empty));
                    item.Level = ilvl;
                    if (navigator.MoveToChild("containerObjects", string.Empty))
                    {
                        navigator.MoveToFirstChild();
                        do
                        {
                            if (navigator.LocalName != "object") continue;
                            string area = navigator.GetAttribute("area", string.Empty);
                            string name = navigator.GetAttribute("name", string.Empty);
                            int minlevel = Int32.Parse(navigator.GetAttribute("minLevel", string.Empty));
                            if (area.Length > 0 && name.Length > 0 && minlevel >= 60)
                            {
                                if (!item.AreaName.Contains(area)) item.AreaName.Add(area);
                                if (!item.CreatureName.Contains(name)) item.CreatureName.Add(name);
                            }
                        }
                        while (navigator.MoveToNext("object", string.Empty));
                        navigator.MoveToParent();
                    }
                    if (navigator.MoveToChild("dropCreatures", string.Empty))
                    {
                        navigator.MoveToFirstChild();
                        do
                        {
                            if (navigator.LocalName != "creature") continue;
                            string area = navigator.GetAttribute("area", string.Empty);
                            string name = navigator.GetAttribute("name", string.Empty);
                            int minlevel = Int32.Parse(navigator.GetAttribute("minLevel", string.Empty));
                            int classification = Int32.Parse(navigator.GetAttribute("classification", string.Empty));
                            if (classification > 0 && minlevel >= 60)
                            {
                                if (!item.AreaName.Contains(area)) item.AreaName.Add(area);
                                if (!item.CreatureName.Contains(name)) item.CreatureName.Add(name);
                            }
                        }
                        while (navigator.MoveToNext("creature", string.Empty)) ;
                        navigator.MoveToParent();
                    }
                }
                catch (Exception)
                {
                    Console.WriteLine("Error: " + item.Id);
                }
            }

        }
        public void updateFiles(string path)
        {
            DirectoryInfo myInfo = new DirectoryInfo(path);
            DirectoryInfo myInfoItem = new DirectoryInfo(path+"//info//");
            FileInfo[] myFiles = myInfo.GetFiles("i*.xml");
            FileInfo[] myFilesItem = myInfoItem.GetFiles("i*.xml");
            List<int> numbers = new List<int>();
            List<int> numbersItem = new List<int>();
            for (int i = 0; i < myFiles.Length; i++)
            {
                int number = Int32.Parse(myFiles[i].Name.Substring(1, 5));
                numbers.Add(number);
            }
            for (int i = 0; i < myFilesItem.Length; i++)
            {
                int number = Int32.Parse(myFilesItem[i].Name.Substring(1, 5));
                numbersItem.Add(number);
            }
            for (int i = 30000; i < 34000; i++)
            {
                if (numbers.Contains(i) && numbersItem.Contains(i)) continue;
                else
                {
                    bool resultus = parseWeb(i, true,false);
                    if (!resultus)
                    {
                        bool resulteu = parseWeb(i, true, true);
                        if (resulteu)
                        {
                            parseWeb(i, false, true);
                        }
                    }
                    else
                    {
                        parseWeb(i, false, false);
                    }
                }
                Thread.Sleep(250);
                Console.WriteLine(i);
            }
        }
        private void getItemLevels()
        {
            DirectoryInfo myInfo = new DirectoryInfo(ITEMPATH+"\\info\\");
            FileInfo[] myFiles = myInfo.GetFiles("i*.xml");
            List<int> numbers = new List<int>();
            for (int i = 0; i < myFiles.Length; i++)
            {
                int number = Int32.Parse(myFiles[i].Name.Substring(1, 5));
                numbers.Add(number);
            }
            for (int i = 33000; i < 34000; i++)
            {
                if (numbers.Contains(i)) continue;
                else parseWeb(i, false, false);
                Thread.Sleep(250);
                Console.WriteLine(i);
            }
        }
        private static void parseBuild(ref ParsedCharacter pc, int[] build)
        {
            Assembly myAsm = Assembly.GetExecutingAssembly();
            List<string> builds = new List<string>();
            builds = Program.ClassBuildDictionary[pc.Class];
            if (builds.Count == 0) return;
            foreach (string buildstring in builds)
            {
                Character myChar = (Character)myAsm.CreateInstance(myAsm.GetName().Name + "." + pc.Class + "." + buildstring);
                if (myChar.evaluateBuild(build))
                {
                    pc.Build = buildstring;
                    return;
                }
            }

        }
        public static ParsedCharacter parseCharacter(string path)
        {
            ParsedCharacter pc = new ParsedCharacter();
            int[] build = new int[3];
            pc.Items = new GenericItem[Character.SlotCount];
            Regex rchar = new Regex(@"&n=(?<value>\w+)");
            Match mchar = rchar.Match(path);
            if (mchar.Success)
            {
                string charname = mchar.Result("${value}");
            }
            else
            {
                return pc;
            }
            XPathNodeIterator itemChildren;
            try
            {
                HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(path);
                httpRequest.UserAgent = @"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
                httpRequest.Timeout = 30000;
                HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
                XPathDocument document = new XPathDocument(httpResponse.GetResponseStream());
                XPathNavigator navigator = document.CreateNavigator();
                httpResponse.Close();
                navigator.MoveToChild("page", string.Empty);
                navigator.MoveToChild("characterInfo", string.Empty);
                navigator.MoveToChild("character", string.Empty);
                pc.Class = navigator.GetAttribute("class", string.Empty);
                navigator.MoveToParent();
                navigator.MoveToChild("characterTab", string.Empty);
                navigator.MoveToChild("talentSpec", string.Empty);
                build[0] = Int32.Parse(navigator.GetAttribute("treeOne", string.Empty));
                build[1] = Int32.Parse(navigator.GetAttribute("treeTwo", string.Empty));
                build[2] = Int32.Parse(navigator.GetAttribute("treeThree", string.Empty));
                navigator.MoveToParent();
                navigator.MoveToChild("items", string.Empty);
                itemChildren = navigator.SelectChildren(string.Empty, string.Empty);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return pc;
            }
            foreach (XPathNavigator itemelement in itemChildren)
            {
                int itemid = Int32.Parse(itemelement.GetAttribute("id", string.Empty));
                int slot = Int32.Parse(itemelement.GetAttribute("slot", string.Empty));
                int[] gems = new int[3];
                int slotid = -1;
                int gemcount = 0;
                for (int i = 0; i < gems.Length; i++)
                {
                    int gemid = Int32.Parse(itemelement.GetAttribute("gem" + i + "Id", string.Empty));
                    if (gemid > 0)
                    {
                        gems[i] = gemid;
                        gemcount++;
                    }
                }
                if (slot >= 0 && slot <= 18)
                {
                    int[] slotconversion = new int[] { 0, 1, 2, -1, 4, 7, 8, 9, 5, 6, 10, 11, 12, 13, 3, 14, 15, 16, -1 };
                    slotid = slotconversion[slot];
                }

                if (slotid != -1)
                {
                    pc.Items[slotid].Gem0Id = gems[0];
                    pc.Items[slotid].Gem1Id = gems[1];
                    pc.Items[slotid].Gem2Id = gems[2];
                    pc.Items[slotid].Id = itemid;
                }
            }
            parseBuild(ref pc,build);
            return pc;
        }
    }
}